package com.lemon.boot.common.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.lemon.boot.common.consts.RedisKeyConst;
import com.lemon.boot.common.entity.LoginUserDetail;
import com.lemon.boot.common.properties.JwtConfigProperties;
import com.lemon.boot.common.service.RedisService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 李猛
 * @datetime 2024/1/12 9:52
 * @description 类对象
 */
public class JwtUtil {
    /**
     * 令牌加密算法
     */
    public final static SecureDigestAlgorithm<SecretKey, SecretKey> SECURE_ALGORITHM = Jwts.SIG.HS256;

    /**
     * 从spring容器中获取配置信息
     */
    public static final JwtConfigProperties JWT_CONFIG_PROPERTIES = SpringUtil.getBean(JwtConfigProperties.class);
    /**
     * 密钥
     */
    public final static SecretKey SECRET_KEY = Keys.hmacShaKeyFor(JWT_CONFIG_PROPERTIES.getSecret().getBytes());

    /**
     * 获取HttpServletRequest
     *
     * @return
     */
    public static HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        RequestContextHolder.setRequestAttributes(attributes, true);
        return attributes.getRequest();
    }

    /**
     * 获取token
     *
     * @return
     */
    public static String getToken() {
        return getRequest().getHeader(JWT_CONFIG_PROPERTIES.getHeader());
    }

    /**
     * 获取token
     *
     * @return
     */
    public static String getToken(HttpServletRequest request) {
        return request.getHeader(JWT_CONFIG_PROPERTIES.getHeader());
    }

    /**
     * 创建 token
     *
     * @return
     */
    public static String createToken(LoginUserDetail user) {
        //1.登录的用户信息转map
        Map<String, Object> userMap = ConverterUtil.toMap(user);

        //2.过期时间
        Date now = new Date();//当前时间
        Date exprireDate = DateUtil.offsetMinute(now, JWT_CONFIG_PROPERTIES.getExpireTime());

        //3.令牌ID
        String id = IdUtil.getSnowflakeNextIdStr();

        //4.创建JWT Token，并返回
        return Jwts.builder()
                .header()// 设置头部信息header
                .add("typ", "JWT")
                .add("alg", "HS256")
                .and()
                .claims(userMap)//设置自定义负载信息payload
                .id(id)//令牌ID
                .expiration(exprireDate)//过期日期，要大于签发时间
                .issuedAt(now)//签发时间
                .subject(user.getUsername())//主题
                .issuer(user.getName())//签发者
                .signWith(SECRET_KEY, SECURE_ALGORITHM)//签名
                .compact();
    }

    /**
     * 解析token
     *
     * @param token
     * @return
     */
    public static Jws<Claims> parseToken(String token) {
        return Jwts.parser().verifyWith(SECRET_KEY).build().parseSignedClaims(token);
    }

    /**
     * 判断token是否有效
     *
     * @param token
     * @return
     */
    public static boolean isValid(String token) {
        return !isExpired(token);
    }

    /**
     * 刷新token
     *
     * @param token
     * @return
     */
    public static String refreshToken(String token) {
        return createToken(getCurrentUser(token));
    }

    /**
     * 判断token是否过期
     *
     * @param token
     * @return
     */
    public static boolean isExpired(String token) {
        return parseToken(token).getPayload().getExpiration().before(new Date());
    }

    /**
     * 根据token获取载荷信息
     *
     * @param token
     * @return
     */
    public static LoginUserDetail getCurrentUser(String token) {
        Claims payload = parseToken(token).getPayload();
        LoginUserDetail user = new LoginUserDetail();
        user.setUserId(payload.get("userId", Long.class));
        user.setTenantId(payload.get("tenantId", Long.class));
        user.setName(payload.get("name", String.class));
        user.setUsername(payload.get("username", String.class));
        user.setPassword(payload.get("password", String.class));
        user.setEnabled(payload.get("enabled", Boolean.class));
        List<?> roleIdList = payload.get("roleIdList", ArrayList.class);
        user.setRoleIdList(roleIdList.stream().map(Object::toString).map(NumberUtil::parseLong).collect(Collectors.toList()));
        user.setAuthorities(getAuthoritiesByRedis(user.getUsername()));
        return user;
    }

    /**
     * 获取登录用户信息
     *
     * @return
     */
    public static LoginUserDetail getCurrentUser() {
        return getCurrentUser(getToken());
    }

    /**
     * 获取登录用户信息
     *
     * @return
     */
    public static LoginUserDetail getCurrentUser(HttpServletRequest request) {
        return getCurrentUser(getToken(request));
    }

    /**
     * 根据用户名获取权限
     *
     * @param username
     * @return
     */
    private static List<SimpleGrantedAuthority> getAuthoritiesByRedis(String username) {
        //1.从 spring ioc 容器中获取 redisService
        RedisService redisService = SpringUtil.getBean(RedisService.class);

        //2.获取用户权限
        String key = String.format("%s%s", RedisKeyConst.REDIS_KEY_LOGIN_USER, username);
        ArrayList<?> resourceList = redisService.get(key, ArrayList.class);
        if (CollectionUtils.isEmpty(resourceList)) {
            return new ArrayList<>(0);
        }
        //3.将权限字符串转成SimpleGrantedAuthority对象
        return resourceList.stream().map(Object::toString).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    }
}
